ina226.py
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
import time
import math
import ctypes
import sys
PYTHON_SMBUS_LIB_PRESENT = True
PYTHON_AARDVARK_LIB_PRESENT = False
try:
import smbus
except ImportError as e:
PYTHON_SMBUS_LIB_PRESENT = False
try:
import pyaardvark
except ImportError as e:
PYTHON_AARDVARK_LIB_PRESENT = False
INA226_ADDRESS =(0x40)
INA226_REG_CONFIG =(0x00)
INA226_REG_SHUNTVOLTAGE =(0x01)
INA226_REG_BUSVOLTAGE =(0x02)
INA226_REG_POWER =(0x03)
INA226_REG_CURRENT =(0x04)
INA226_REG_CALIBRATION =(0x05)
INA226_REG_MASKENABLE =(0x06)
INA226_REG_ALERTLIMIT =(0x07)
INA226_BIT_SOL =(0x8000)
INA226_BIT_SUL =(0x4000)
INA226_BIT_BOL =(0x2000)
INA226_BIT_BUL =(0x1000)
INA226_BIT_POL =(0x0800)
INA226_BIT_CNVR =(0x0400)
INA226_BIT_AFF =(0x0010)
INA226_BIT_CVRF =(0x0008)
INA226_BIT_OVF =(0x0004)
INA226_BIT_APOL =(0x0002)
INA226_BIT_LEN =(0x0001)
#enum replacement, but not truly
#now replaced class by dict because it can give me back keys
ina226_averages_t = dict(
INA226_AVERAGES_1 = 0b000,
INA226_AVERAGES_4 = 0b001,
INA226_AVERAGES_16 = 0b010,
INA226_AVERAGES_64 = 0b011,
INA226_AVERAGES_128 = 0b100,
INA226_AVERAGES_256 = 0b101,
INA226_AVERAGES_512 = 0b110,
INA226_AVERAGES_1024 = 0b111)
ina226_busConvTime_t = dict(
INA226_BUS_CONV_TIME_140US = 0b000,
INA226_BUS_CONV_TIME_204US = 0b001,
INA226_BUS_CONV_TIME_332US = 0b010,
INA226_BUS_CONV_TIME_588US = 0b011,
INA226_BUS_CONV_TIME_1100US = 0b100,
INA226_BUS_CONV_TIME_2116US = 0b101,
INA226_BUS_CONV_TIME_4156US = 0b110,
INA226_BUS_CONV_TIME_8244US = 0b111)
ina226_shuntConvTime_t = dict(
INA226_SHUNT_CONV_TIME_140US = 0b000,
INA226_SHUNT_CONV_TIME_204US = 0b001,
INA226_SHUNT_CONV_TIME_332US = 0b010,
INA226_SHUNT_CONV_TIME_588US = 0b011,
INA226_SHUNT_CONV_TIME_1100US = 0b100,
INA226_SHUNT_CONV_TIME_2116US = 0b101,
INA226_SHUNT_CONV_TIME_4156US = 0b110,
INA226_SHUNT_CONV_TIME_8244US = 0b111)
ina226_mode_t = dict(
INA226_MODE_POWER_DOWN = 0b000,
INA226_MODE_SHUNT_TRIG = 0b001,
INA226_MODE_BUS_TRIG = 0b010,
INA226_MODE_SHUNT_BUS_TRIG = 0b011,
INA226_MODE_ADC_OFF = 0b100,
INA226_MODE_SHUNT_CONT = 0b101,
INA226_MODE_BUS_CONT = 0b110,
INA226_MODE_SHUNT_BUS_CONT = 0b111)
# available options are 'AARDVARK','SBC_LINUX_SMBUS'
I2C_DRIVER = 'AARDVARK'
# other I2C options
I2C_DEFAULT_CLK_KHZ = 100
I2C_DEFAULT_BUS_NUMBER = 0
class ina226:
def __init__(self,ina226_addr = INA226_ADDRESS, i2c_bus_number=I2C_DEFAULT_BUS_NUMBER, i2c_clk_Khz=I2C_DEFAULT_CLK_KHZ, i2c_driver_type = I2C_DRIVER):
self.i2c_bus = smbus.SMBus(i2c_bus_number)
self.readRegister16 = self.readRegister16_SMBUS
self.writeRegister16 = self.writeRegister16_SMBUS
if i2c_clk_Khz != I2C_DEFAULT_CLK_KHZ:
print 'Python SMBUS linux driver doesn\'t provide I2C CLK Freq Manipulation support yet,'
print 'So Ignoring i2c_clk_khz param and using default.'
self.ina226_address = ina226_addr
self.vBusMax = 36
self.vShuntMax = 0.08192
self.rShunt = 0.1
self.currentLSB = 0
self.powerLSB = 0
self.iMaxPossible = 0
#not using with statement related code yet
#this causes some issue may be because when exception occurs I am manually calling
#self.close() and even this function tries to call the same. Need to check.
#def __del__(self):
# self.close()
def close(self):
self.i2c_bus.close()
def readRegister16_SMBUS(self,register):
#higher_byte = self.i2c_bus.read_byte_data(self.ina226_address,register)
#lower_byte = self.i2c_bus.read_byte_data(self.ina226_address,register+1)
data = self.i2c_bus.read_i2c_block_data(self.ina226_address,register,2)
higher_byte = data[0]
lower_byte = data[1]
#there is still some issue in read which we need to fix, we are not able to print negative current--done--fixed using ctypes int16 return
word_data = higher_byte << 8 | lower_byte
#return word_data
return ctypes.c_int16(word_data).value
def writeRegister16_SMBUS(self,register,dataWord):
higher_byte = (dataWord >> 8) & 0xff
lower_byte = dataWord & 0xff #truncating the dataword to byte
self.i2c_bus.write_i2c_block_data(self.ina226_address,register,[higher_byte,lower_byte])
def readRegister16_AARDVARK(self,register):
#higher_byte = self.i2c_bus.read_byte_data(self.ina226_address,register)
#lower_byte = self.i2c_bus.read_byte_data(self.ina226_address,register+1)
#data = self.i2c_bus.read_i2c_block_data(self.ina226_address,register,2)
register_addr_str = chr(register)
byte_char_data = self.i2c_bus.i2c_master_write_read(self.ina226_address,register_addr_str,2)
data = [ord(b) for b in byte_char_data]
higher_byte = data[0]
lower_byte = data[1]
#there is still some issue in read which we need to fix, we are not able to print negative current--done--fixed using ctypes int16 return
word_data = higher_byte << 8 | lower_byte
#return word_data
return ctypes.c_int16(word_data).value
#if this does not work as expected than we should try to read bytes and they convert into word as in Arduino
#return self.i2c_bus.read_word_data(self.ina226_address,register)
def writeRegister16_AARDVARK(self,register,dataWord):
higher_byte = (dataWord >> 8) & 0xff
lower_byte = dataWord & 0xff #truncating the dataword to byte
data = (register, higher_byte, lower_byte)
data = ''.join(chr(c) for c in data)
self.i2c_bus.i2c_master_write(self.ina226_address,data)
#self.i2c_bus.write_i2c_block_data(self.ina226_address,register,[higher_byte,lower_byte])
#doesn't work
#self.i2c_bus.write_byte_data(self.ina226_address,register,higher_byte)
#self.i2c_bus.write_byte_data(self.ina226_address,register+1,lower_byte)
#if this does not work as expected than we should try to read bytes and they convert into word as in Arduino
#self.i2c_bus.write_word_data(self.ina226_address,register,dataWord)
def configure(self,avg = ina226_averages_t['INA226_AVERAGES_1'], busConvTime = ina226_busConvTime_t['INA226_BUS_CONV_TIME_1100US'], shuntConvTime = ina226_shuntConvTime_t['INA226_SHUNT_CONV_TIME_1100US'], mode = ina226_mode_t['INA226_MODE_SHUNT_BUS_CONT']):
config = 0
config |= (avg << 9 | busConvTime << 6 | shuntConvTime << 3 | mode)
self.writeRegister16(INA226_REG_CONFIG, config)
return True
def calibrate(self,rShuntValue = 0.1, iMaxExcepted = 2):
self.rShunt = rShuntValue
self.iMaxPossible = self.vShuntMax / self.rShunt
minimumLSB = float(iMaxExcepted) / 32767
#print "minimumLSB:"+str(minimumLSB)
self.currentLSB = int((minimumLSB * 100000000))
#print "currentLSB:"+str(self.currentLSB)
self.currentLSB /= 100000000.0
self.currentLSB /= 0.0001
self.currentLSB = math.ceil(self.currentLSB)
self.currentLSB *= 0.0001
self.powerLSB = self.currentLSB * 25;
#print "powerLSB:"+str(self.powerLSB)
#print "rshunt:"+str(self.rShunt)
calibrationValue = int(((0.00512) / (self.currentLSB * self.rShunt))) #if we get error need to convert this to unsigned int 16 bit instead
self.writeRegister16(INA226_REG_CALIBRATION, calibrationValue)
return True
def getAverages(self):
value = self.readRegister16(INA226_REG_CONFIG)
value &= 0b0000111000000000
value >>= 9
return value
def getMaxPossibleCurrent(self):
return (self.vShuntMax / self.rShunt)
def getMaxCurrent(self):
maxCurrent = (self.currentLSB * 32767)
maxPossible = self.getMaxPossibleCurrent()
if maxCurrent > maxPossible:
return maxPossible
else:
return maxCurrent
def getMaxShuntVoltage(self):
maxVoltage = self.getMaxCurrent() * self.rShunt
if maxVoltage >= self.vShuntMax:
return self.vShuntMax
else:
return maxVoltage
def getMaxPower(self):
return (self.getMaxCurrent() * self.vBusMax)
def readBusPower(self):
return (self.readRegister16(INA226_REG_POWER) * self.powerLSB)
def readShuntCurrent(self):
return (self.readRegister16(INA226_REG_CURRENT) * self.currentLSB)
def readShuntVoltage(self):
voltage = self.readRegister16(INA226_REG_SHUNTVOLTAGE)
return (voltage * 0.0000025)
def readBusVoltage(self):
voltage = self.readRegister16(INA226_REG_BUSVOLTAGE)
return (voltage * 0.00125)
def getBusConversionTime(self):
value = self.readRegister16(INA226_REG_CONFIG)
value &= 0b0000000111000000
value >>= 6
return value
def getShuntConversionTime(self):
value = self.readRegister16(INA226_REG_CONFIG)
value &= 0b0000000000111000
value >>= 3
return value
def getMode(self):
value = self.readRegister16(INA226_REG_CONFIG)
value &= 0b0000000000000111
return value
def setMaskEnable(self, mask):
self.writeRegister16(INA226_REG_MASKENABLE, mask)
def getMaskEnable(self):
return self.readRegister16(INA226_REG_MASKENABLE)
def enableShuntOverLimitAlert(self):
self.writeRegister16(INA226_REG_MASKENABLE, INA226_BIT_SOL)
def enableBusOverLimitAlert(self):
self.writeRegister16(INA226_REG_MASKENABLE, INA226_BIT_BOL)
def enableBusUnderLimitAlert(self):
self.writeRegister16(INA226_REG_MASKENABLE, INA226_BIT_BUL)
def enableOverPowerLimitAlert(self):
self.writeRegister16(INA226_REG_MASKENABLE, INA226_BIT_POL)
def enableConversionReadyAlert(self):
self.writeRegister16(INA226_REG_MASKENABLE, INA226_BIT_CNVR)
def setBusVoltageLimit(self, voltage):
value = voltage / 0.00125
self.writeRegister16(INA226_REG_ALERTLIMIT, value)
def setShuntVoltageLimit(self, voltage):
value = voltage * 25000
self.writeRegister16(INA226_REG_ALERTLIMIT, value)
def setPowerLimit(self, watts):
value = watts / self.powerLSB
self.writeRegister16(INA226_REG_ALERTLIMIT, value)
def setAlertInvertedPolarity(self, inverted):
temp = self.getMaskEnable()
if (inverted):
temp |= INA226_BIT_APOL;
else:
temp &= ~INA226_BIT_APOL;
self.setMaskEnable(temp)
def setAlertLatch(self, latch):
temp = self.getMaskEnable()
if (latch):
temp |= INA226_BIT_LEN
else:
temp &= ~INA226_BIT_LEN
self.setMaskEnable(temp)
def isMathOverflow(self):
return ((self.getMaskEnable() & INA226_BIT_OVF) == INA226_BIT_OVF)
def isAlert(self):
return ((self.getMaskEnable() & INA226_BIT_AFF) == INA226_BIT_AFF)
import sys
iSensor = ina226(INA226_ADDRESS,1)
iSensor.configure(avg = ina226_averages_t['INA226_AVERAGES_4'],)
iSensor.calibrate(rShuntValue = 0.02, iMaxExcepted = 2)
time.sleep(1)
batteryPercentage = ((round(iSensor.readBusVoltage(),3)-10.5) / (14.3-10.5)) * 100.0
sys.stdout.write(str(round(iSensor.readShuntCurrent(),3)) +' '+ str(round(iSensor.readBusVoltage(),1)) +' '+ str(round(iSensor.readBusPower(),1))+' ' + str(round(batteryPercentage,1)) +'\n')